﻿using System.Runtime.Remoting.Messaging;
using System;
using System.Linq;

namespace AspectIntercept
{
    public class InterceptSink : IMessageSink
    {
        private IMessageSink nextSink;

        public Boolean InterceptAllMethods { get; set; }
        public Type PreProcesor { get; set; }
        public Type PostProcesor { get; set; }
        public String[] OmitMethods { get; set; }

        public InterceptSink(IMessageSink nextSink)
        {
            this.nextSink = nextSink;
        }

        #region IMessageSink Members

        public IMessage SyncProcessMessage(IMessage msg)
        {
            IMethodCallMessage mcm = (msg as IMethodCallMessage);
            this.PreProcess(ref mcm);
            IMessage rtnMsg = nextSink.SyncProcessMessage(msg);
            IMethodReturnMessage mrm = (rtnMsg as IMethodReturnMessage);
            this.PostProcess(msg as IMethodCallMessage, ref mrm);
            return mrm;
        }

        public IMessageSink NextSink
        {
            get
            {
                return this.nextSink;
            }
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            IMessageCtrl rtnMsgCtrl = nextSink.AsyncProcessMessage(msg, replySink);
            return rtnMsgCtrl;
        }

        #endregion

        private void PreProcess(ref IMethodCallMessage msg)
        {
            if (OmitMethods.Contains(msg.MethodName)) return;
            if (msg.MethodBase.GetCustomAttributes(typeof(InterceptOmitAttribute), true).Length > 0) return; 
            if (InterceptAllMethods && PreProcesor != null)
            {
                PreProcessAttribute attr = new PreProcessAttribute(PreProcesor);
                attr.Processor.Process(ref msg);
            }
            else
            {
                PreProcessAttribute[] attrs
                    = (PreProcessAttribute[])msg.MethodBase.GetCustomAttributes(typeof(PreProcessAttribute), true);
                for (int i = 0; i < attrs.Length; i++)
                    attrs[i].Processor.Process(ref msg);
            }
        }

        private void PostProcess(IMethodCallMessage callMsg, ref IMethodReturnMessage rtnMsg)
        {
            if (OmitMethods.Contains(callMsg.MethodName)) return;
            if (callMsg.MethodBase.GetCustomAttributes(typeof(InterceptOmitAttribute), true).Length > 0) return;
            if (InterceptAllMethods && PostProcesor != null)
            {
                PostProcessAttribute attr = new PostProcessAttribute(PostProcesor);
                attr.Processor.Process(callMsg, ref rtnMsg);
            }
            else
            {
                PostProcessAttribute[] attrs
                 = (PostProcessAttribute[])callMsg.MethodBase.GetCustomAttributes(typeof(PostProcessAttribute), true);
                for (int i = 0; i < attrs.Length; i++)
                    attrs[i].Processor.Process(callMsg, ref rtnMsg);
            }

        }

    }

}
